可不可以給我一篇文章的時間? 來,setTimeout
時間從何而來?有看過星際效應嗎?在不同的星球時間也會不一樣,在 javascript 亦是如此....
不是拉!
這次會想寫是因為一直以為自己有理解 setTimeout()
搭配作用域的萬年題目,結果卻發現居然寫不出來,然後 setInterval()
也沒有實際用過,所以就來研究一下,順便重新理解 event loop。
setTimeout()
與 setInterval()
的全名應該是: window.setTimeout()
、 window.setInterval()
,他們是一種 webAPI,是瀏覽器提供的函式可以讓我們去使用。
先來介紹一下這兩個的差異:
setTimeout()
:定時器,設定多久時間之後呼叫callback function,然後就結束了。
setInterval()
:週期性定時器,設定多久時間呼叫一次callback function,沒有設定終止條件的話會一直週期性呼叫。
clearTimeout()
及 clearInterval()
。var timeoutID = scope.setTimeout(function[, delay]);
setTimeout 會回傳一個 正整數 ,這個整數是為了結束之後可以清理 stack 不會佔空間,或者有可能影響到其他的所以提供清除的方法。
會讓你傳的callback function 在 delay 的時間過後執行,
這裡最特別的事情是 setTimeout 是非同步的,然而 javascript 是單一執行緒,也就是說會一行一行執行下來,當遇到 setTimeout 的時候是會被告知說現在等 1 秒再執行裡面的 function,這個時候 javascript 為了讓畫面可以繼續執行,不會被這件事 blocking(堵塞)住,會把 setTimeout 放到 webAPI 等待,等到單一執行序的 stack 都結束了,就再把 setTimeout 裡的 callback function 放到 task quene ,1 秒之後執行。
來自 所以說event loop到底是什麼玩意兒?| Philip Roberts | JSConf EU 影片截圖
這就是 event loop ,更詳細可以去看這支影片:所以說event loop到底是什麼玩意兒?| Philip Roberts | JSConf EU
實際來看看差別:
console.log('start')
setTimeout(() => {
console.log('Its setTimeout')
}, 1000)
console.log('end')
// start
// end
(一秒之後)
// Its setTimeout
javascript 會把現在可以執行的都執行完,等到裡面沒東西了,才會去執行 setTimeout 的 callback。
在講這個題目之前,應該要釐清一些問題:
for...loop
是一個暴露的狀態,正常都會用 function 包起來,況且現在也沒有人會用 var 宣告了。這個題目本來就不是正常的樣貌,就像那些 this 的題目,真的會有人在物件裏面那麼多層然後用 this 嗎? 雖然不是正常的樣貌,但還是要學習去拆解它,找到正確的做法。
在 setTimeout()
裡面做一個 for...loop
,希望可以印出 1, 2, 3, 4, 5。
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i)
},1000)
}
// 5, 5, 5, 5, 5
大家應該都知道這個狀況只要改成 let 就好了,這是因為 let 會讓變數鎖在作用域裡面,也就是 {}
,但是 var 只能鎖在 function 作用域裡面,但現在沒有在 function 裡面嘛,所以 for...loop
就很快速地跑完了,然後 setTimeout 剛不是說會放在 task quene 等待 stack 都結束才會執行嗎? 所以等到 1 秒之後, i
早就變成 5 了。
啊啊,我知道,就是用一個 IIFE 包起來就好了嘛,但要怎麼包?給你五秒想一下。
1
2
3
4
5
曾經我也以為我會,但是真的要寫出來的時候發現腦袋空空,IIFE也不熟,不知道該怎麼放? 那就直接用一個 function 包起來,再呼叫他吧!
for (var i = 0; i < 5; i++) {
function count(i) {
setTimeout(() => {
console.log(i)
},1000)
}
count(i)
}
// 0, 1, 2, 3, 4
解釋一下過程:
for...loop
跑第一個 i = 0
的時候,進入 count(i)
裡面,此時 i = 0
的值已經存入 function count(i)
作用域裡面。i
,因為傳了 5 次 i
進去 function count(i)
裡面,也都被儲存起來,當 stack 都結束之後,task quene 就會送出這五個 console.log(i)
出去。用 IIFE 來做:
for (var i = 0; i < 5; i++) {
(function(i) {
setTimeout(() => {
console.log(i)
},1000)
})(i)
}
// 0, 1, 2, 3, 4
經過了剛剛直接寫一個 function 之後應該有比較好理解 IIFE 為什麼要這麼寫了吧!
雖然知道要用 let 就可以解決這個問題,但還是要理解作用域的關係去解決問題。
不要死背啊,像當初我一樣
var intervalID = setInterval(function[, delay]);
使用方式跟 setTimeout 一模一樣,差別就是 setInterval 會在給的時間間隔不斷執行 callback function,我覺得 setInterval 可能比較適合使用清除的方法,因為如果 callback 沒有寫判斷式的話真的會執行到天荒地老啊。
立即來試試看:
const intervalId = setInterval(() => {
console.log('setInterval')
}, 1000)
clearInterval(intervalId)
會在一秒之後印出 'setInterval'
,得趕緊拿到 intervalId
,然後執行 clearInterval(intervalId)
才能停止。
setTimeInterval()
很適合做一個倒數計時啊!
<div>
<p>倒數計時 90 秒</p>
<p>還剩下:</p>
<span></span>
<span>秒</span>
</div>
const span = document.querySelector('span')
let time = 91;
setInterval(() => {
if(time > 0) {
time = time - 1;
} else {
time = 'time out'
return;
}
span.textContent = time;
}, 1000) ;
codepen: https://codepen.io/Jadddxx/pen/dyeaojp?editors=1010
參考資料:
JavaScript 中的同步與非同步(上):先成為 callback 大師吧!
所以說event loop到底是什麼玩意兒?| Philip Roberts | JSConf EU
談談 JavaScript 的 setTimeout 與 setInterval
JS设置定时器和清除定时器
MDN window.setTimeout
MDN setInterval()
w3c Window setInterval()
w3c Window setTimeout()